import asyncio
import configparser
from msgraph.generated.models.o_data_errors.o_data_error import ODataError
from azure.identity import DeviceCodeCredential
from msgraph import GraphServiceClient
from graph import Graph

async def greet_user(graph: Graph):
    user = await graph.get_user()
    if user:
        print('Hello,', user.display_name)
        # For Work/school accounts, email is in mail property
        # Personal accounts, email is in userPrincipalName
        print('Email:', user.mail or user.user_principal_name, '\n')

async def list_inbox(graph: Graph):
    message_page = await graph.get_inbox()
    if message_page and message_page.value:
        # Output each message's details
        for message in message_page.value:
            print('Message:', message.subject)
            if (
                message.from_ and
                message.from_.email_address
            ):
                print('  From:', message.from_.email_address.name or 'NONE')
            else:
                print('  From: NONE')
            print('  Status:', 'Read' if message.is_read else 'Unread')
            print('  Received:', message.received_date_time)

        # If @odata.nextLink is present
        more_available = message_page.odata_next_link is not None
        print('\nMore messages available?', more_available, '\n')

async def send_mail(graph: Graph):
    """
    发送邮件功能 - 询问用户输入邮件地址、主题和内容
    """
    try:
        # 获取收件人邮箱地址
        recipient = input('请输入收件人邮箱地址: ').strip()
        if not recipient:
            print('邮箱地址不能为空！\n')
            return
        
        # 获取邮件主题
        subject = input('请输入邮件主题: ').strip()
        if not subject:
            print('邮件主题不能为空！\n')
            return
        
        # 获取邮件内容
        print('请输入邮件内容（输入完成后按回车）:')
        body = input().strip()
        if not body:
            print('邮件内容不能为空！\n')
            return
        
        # 发送邮件
        await graph.send_mail(subject, body, recipient)
        print(f'邮件已成功发送到 {recipient}！\n')
        
    except Exception as e:
        print(f'发送邮件时出错: {str(e)}\n')

async def display_access_token(graph: Graph):
    token = await graph.get_user_token()
    print('User token:', token, '\n')
    
async def reauthenticate_with_device_code(graph: Graph):
    """
    重新使用设备代码流进行身份验证
    """
    print('正在重新进行设备代码身份验证...')
    # 重新创建设备代码凭据
    client_id = graph.settings['clientId']
    tenant_id = graph.settings['tenantId']
    graph_scopes = graph.settings['graphUserScopes'].split(' ')
    
    graph.device_code_credential = DeviceCodeCredential(client_id, tenant_id=tenant_id)
    graph.user_client = GraphServiceClient(graph.device_code_credential, graph_scopes)
    graph.use_static_token = False
    print('设备代码身份验证已重新配置。')
    
    # 获取并自动保存新token
    print('正在获取新token并自动保存到配置文件...')
    token = await graph.get_user_token()
    # token已在get_user_token方法中自动保存到config.cfg
    print('认证完成，token已更新。\n')

async def main():
    print('Python Graph Tutorial\n')

    # Load settings
    config = configparser.ConfigParser()
    config.read(['config.cfg', 'config.dev.cfg'], encoding='utf-8')
    azure_settings = config['azure']

    # 检查是否有存储的令牌
    has_stored_token = 'token' in azure_settings and azure_settings['token'].strip()
    
    if has_stored_token:
        print('检测到配置文件中有存储的访问令牌。')
        use_stored = input('是否使用存储的令牌？(y/n，默认y): ').strip().lower()
        use_stored_token = use_stored != 'n'
    else:
        print('配置文件中未找到存储的令牌，将使用设备代码流认证。')
        use_stored_token = False

    graph: Graph = Graph(azure_settings, use_stored_token)
    
    # 如果使用设备代码流认证，获取并自动保存token
    if not use_stored_token:
        print('正在获取新token并自动保存到配置文件...')
        token = await graph.get_user_token()
        # token已在get_user_token方法中自动保存到config.cfg

    await greet_user(graph)

    choice = -1

    while choice != 0:
        print('Please choose one of the following options:')
        print('0. Exit')
        print('1. Display access token')
        print('2. List my inbox')
        print('3. Send mail')
        if has_stored_token:
            print('4. Re-authenticate with device code')
            print('5. Make a Graph call')
        else:
            print('4. Make a Graph call')

        try:
            choice = int(input())
        except ValueError:
            choice = -1

        try:
            if choice == 0:
                print('Goodbye...')
            elif choice == 1:
                await display_access_token(graph)
            elif choice == 2:
                await list_inbox(graph)
            elif choice == 3:
                await send_mail(graph)
            elif choice == 4:
                if has_stored_token:
                    await reauthenticate_with_device_code(graph)
                else:
                    await make_graph_call(graph)
            elif choice == 5 and has_stored_token:
                await make_graph_call(graph)
            else:
                print('Invalid choice!\n')
        except ODataError as odata_error:
            print('Error:')
            if odata_error.error:
                print(odata_error.error.code, odata_error.error.message)

if __name__ == '__main__':
    asyncio.run(main())